﻿using System;
using System.Linq;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using GiveAQuiz.Common;
using System.Xml.Linq;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;

namespace GiveAQuiz.Questions
{
  [PartCreationPolicy(CreationPolicy.NonShared)]
  [Export(typeof(IQuestionType))]
  public class MultipleChoiceQuestionType : QuestionType
  {

    DataTemplate _editorTemplate = null;
    UIElement _answerPanel = null;
    ObservableCollection<MultipleChoiceQuestionItem> _answers = new ObservableCollection<MultipleChoiceQuestionItem>();
    Image _thumbnail = null;
    string _question = "";
    ObservableCollection<MultipleChoiceQuestionItem> _choices = new ObservableCollection<MultipleChoiceQuestionItem>();
    ViewCommand _addChoiceCommand = null;
    ViewCommand<MultipleChoiceQuestionItem> _moveUpChoiceCommand = null;
    ViewCommand<MultipleChoiceQuestionItem> _moveDownChoiceCommand = null;
    ViewCommand<MultipleChoiceQuestionItem> _deleteChoiceCommand = null;
    ViewCommand<int> _pickAnswerCommand = null;

    public MultipleChoiceQuestionType()
    {
      _addChoiceCommand = new ViewCommand(() => AddChoice());
      _deleteChoiceCommand = new ViewCommand<MultipleChoiceQuestionItem>(item => DeleteChoice(item),
                                                                         item => Choices.Count > 2);
      _moveUpChoiceCommand =
        new ViewCommand<MultipleChoiceQuestionItem>(item => MoveChoice(item, -1),
                                                    item => Choices.IndexOf(item) > 0);
      _moveDownChoiceCommand =
        new ViewCommand<MultipleChoiceQuestionItem>(item => MoveChoice(item, 1),
                                                    item => Choices.IndexOf(item) < Choices.Count - 1);
      _pickAnswerCommand = new ViewCommand<int>(i =>
        {
          if (i < Answers.Count)
          {
            foreach (var a in Answers) a.IsCorrectChoice = Answers.IndexOf(a) == i;
          }
        });

      // Monitor changes to the choices
      Choices.CollectionChanged += new NotifyCollectionChangedEventHandler(Choices_CollectionChanged);

      // Add default choices
      AddChoice();
      AddChoice();
      Choices[0].IsCorrectChoice = true;

    }

    public override string DisplayName
    {
      get { return "Multiple Choice"; }
    }

    public override UIElement Thumbnail
    {
      get
      {
        if (_thumbnail == null)
        {
          _thumbnail = new Image()
          {
            Source = new BitmapImage(new Uri("/GiveAQuiz.Questions;component/Images/MultipleChoiceQuestionThumbnail.png", UriKind.Relative))
          };
        }
        return _thumbnail;
      }
    }

    public override DataTemplate EditorTemplate
    {
      get
      {
        if (_editorTemplate == null)
        {
          // Using a Helper function to build a DataTemplate for our editor
          // Could create a DataTemplate for this in any way you want
          _editorTemplate = QuestionHelper.GenerateDataTemplateForControl(typeof(MultipleChoiceQuestionEditor));
        }
        return _editorTemplate;
      }
    }

    public override string GetAnswerText(string answerData)
    {
      try
      {
        XDocument doc = XDocument.Parse(answerData);

        var answers = doc.Descendants("Answer").ToList();
        var answer = answers.Where(a => bool.Parse(a.Attribute("Choice").Value)).First();
        return Choices[answers.IndexOf(answer)].ChoiceText;
      }
      catch (Exception)
      {
        return "Failed to get a valid answer.";
      }
    }

    public override QuestionResult GetResult(string answerData)
    {
      var questionResult = QuestionResult.Undetermined;
      try
      {
        XDocument doc = XDocument.Parse(answerData);

        var answers = doc.Descendants("Answer").ToList();
        var answer = answers.Where(a => bool.Parse(a.Attribute("Choice").Value)).First();
        var correctAnswer = Choices.Where(c => c.IsCorrectChoice).First();
        if (answers.IndexOf(answer) == Choices.IndexOf(correctAnswer))
        {
          questionResult = QuestionResult.Correct;
        }
        else
        {
          questionResult = QuestionResult.Incorrect;
        }
      }
      catch
      {
        // NOOP
      }

      return questionResult;
    }

    public override string QuestionText
    {
      get
      {
        return _question;
      }
      set
      {
        if (value != _question)
        {
          _question = value;
          RaisePropertyChanged("QuestionText");
          UpdateDefinition();
        }
      }
    }

    public ObservableCollection<MultipleChoiceQuestionItem> Choices
    {
      get
      {
        return _choices;
      }
    }

    public override string QuestionData
    {
      get
      {
        XDocument doc = new XDocument(
          new XElement("MultipleChoiceQuestionDefinition",
            new XAttribute("QuestionText", QuestionText)));

        // Find the MultipleChoiceQuestionDefinition
        var choices = new XElement("Choices");
        var node = ((XElement)doc.FirstNode);
        node.Add(choices);
        foreach (var choice in this.Choices)
        {
          choices.Add(new XElement("Choice",
            new XAttribute("IsCorrectChoice", choice.IsCorrectChoice),
            new XCData(choice.ChoiceText)));
        }

        return doc.ToString();
      }
      set
      {
        try
        {
          XDocument doc = XDocument.Parse(value);
          var def = doc.Descendants("MultipleChoiceQuestionDefinition").FirstOrDefault();
          if (def != null)
          {
            _question = def.Attribute("QuestionText").Value;

            var choices = def.Descendants("Choice");
            Choices.Clear();
            if (choices != null)
            {
              foreach (var element in choices.ToList())
              {
                Choices.Add(new MultipleChoiceQuestionItem()
                {
                  IsCorrectChoice = bool.Parse(element.Attribute("IsCorrectChoice").Value),
                  ChoiceText = element.Value
                });
              }
            }
          }
        }
        catch (Exception ex)
        {
          throw new Exception("Failed to parse the test data.", ex);
        }
      }
    }

    public override string GetAnswerData()
    {
      XDocument doc = new XDocument(
          new XElement("MultipleChoiceAnswerDefinition"));

      // Find the MultipleChoiceQuestionDefinition
      var choices = new XElement("Answers");
      var node = ((XElement)doc.FirstNode);
      node.Add(choices);
      foreach (var answer in this.Answers)
      {
        choices.Add(new XElement("Answer",
          new XAttribute("Choice", answer.IsCorrectChoice)));
      }

      return doc.ToString();
    }

    public override bool ValidateQuestion()
    {
      bool isValid = true;
      SetErrors("QuestionText", new string[] { });

      if (string.IsNullOrWhiteSpace(QuestionText))
      {
        SetError("QuestionText", "QuestionText is required.");
        isValid = false;
      }

      if (Choices.Any(c => string.IsNullOrWhiteSpace(c.ChoiceText)))
      {
        SetError("QuestionText", "All choices must have a description.");
        isValid = false;
      }

      return isValid;
    }

    public override bool ValidateAnswer()
    {
      ClearErrors("Answers");

      if (!Answers.Any(a => a.IsCorrectChoice))
      {
        SetError("Answers", "You must select at least one correct answer");
        return false;
      }

      return true;
    }

    public override UIElement AnswerPanel
    {
      get
      {
        if (_answerPanel == null)
        {
          _answerPanel = new MultipleChoiceAnswerControl();
        }
        return _answerPanel;
      }
    }

    public override string GetCorrectAnswerText()
    {
      if (Choices != null)
      {
        var correctChoice = Choices.Where(c => c.IsCorrectChoice).First();
        if (correctChoice != null)
        {
          return correctChoice.ChoiceText;
        }
      }
      return "No Correct Choice Possible, error in defintion of test";
    }

    public ObservableCollection<MultipleChoiceQuestionItem> Answers
    {
      get
      {
        if (_answers == null)
        {
          // Make the number of answers based on the choices
          _answers = new ObservableCollection<MultipleChoiceQuestionItem>();
          foreach (var choice in Choices)
          {
            // Create new Items to represent the answers
            _answers.Add(new MultipleChoiceQuestionItem()
            {
              ChoiceText = choice.ChoiceText,
              IsCorrectChoice = false
            });
          }
        }
        return _answers;
      }
    }

    public ViewCommand AddChoiceCommand
    {
      get { return _addChoiceCommand; }
    }

    public ViewCommand<MultipleChoiceQuestionItem> DeleteChoiceCommand
    {
      get { return _deleteChoiceCommand; }
    }

    public ViewCommand<MultipleChoiceQuestionItem> MoveUpChoiceCommand
    {
      get { return _moveUpChoiceCommand; }
    }

    public ViewCommand<MultipleChoiceQuestionItem> MoveDownChoiceCommand
    {
      get { return _moveDownChoiceCommand; }
    }

    public ViewCommand<int> PickAnswerCommand
    {
      get { return _pickAnswerCommand; }
    }


    void AddChoice()
    {
      Choices.Add(new MultipleChoiceQuestionItem() { ChoiceText = "", IsCorrectChoice = false });
    }

    void DeleteChoice(MultipleChoiceQuestionItem item)
    {
      Choices.Remove(item);
    }

    void MoveChoice(MultipleChoiceQuestionItem item, int direction)
    {
      int currentPosition = Choices.IndexOf(item);
      Choices.Remove(item);
      Choices.Insert(currentPosition + direction, item);
    }


    // Handle the choices changing and deal with the event handler for
    // PropertyChanged going in and out
    void Choices_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
      // force recreation of the answers
      _answers = null;

      // Handle the change of the choices.
      if (e.NewItems != null)
      {
        foreach (var item in e.NewItems.Cast<MultipleChoiceQuestionItem>())
        {
          item.PropertyChanged += choice_PropertyChanged;
        }
      }

      // Clean up any deleted choices
      if (e.OldItems != null)
      {
        foreach (var item in e.OldItems.Cast<MultipleChoiceQuestionItem>())
        {
          item.PropertyChanged -= choice_PropertyChanged;
        }
      }

      // Set up the Commands based on the new data
      MoveDownChoiceCommand.RaiseCanExecuteChanged();
      MoveUpChoiceCommand.RaiseCanExecuteChanged();
      DeleteChoiceCommand.RaiseCanExecuteChanged();
    }

    void choice_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
      UpdateDefinition();
    }


  }
}
